package mhserver;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;

/**
 * A single ClientListener thread runs for every user. IMPORTANT: the user won't be add to the users list until he
 * specify his name with the message "name:username" where username is the character name.
 * 
 * @author Gianmarco, Michele
 * 
 */
public class ClientListener implements Runnable {

	/**
	 * The socket used to connect to the server
	 */
	private final Socket				clientSocket;			// connection socket
	/**
	 * The remote ip of the client
	 */
	private final InetAddress			clientIp;				// ip client
	private InputStreamReader			reader;				// incoming flux
	private OutputStreamWriter			writer;				// outgoing flux

	private Thread						mythread;
	private boolean						stopThread	= false;	// to kill this thread...

	private long						startedAt, uptime;
	private boolean						activeUser	= false;	//it will become true when the user will be insert in the player list

	private String						playerName;

	private final MagicHogwartsServer	father;

	/**
	 * Default constructor, initialize all the network stuff.
	 * 
	 * @param myFather
	 *            The <code>ISkeletonServer</code> that contains this <code>ClientListener</code>
	 * @param mySocket
	 *            The <code>Socket</code> used to talk with the server.
	 */
	public ClientListener(Socket mySocket, MagicHogwartsServer server) {
		this.father = server;
		this.clientSocket = mySocket;
		this.clientIp = this.clientSocket.getInetAddress();

		System.out.println("[MHServer] New client with IP: " + this.clientIp.getHostAddress());

		try {
			this.reader = new InputStreamReader(this.clientSocket.getInputStream());
			this.writer = new OutputStreamWriter(this.clientSocket.getOutputStream());
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		this.startedAt = System.currentTimeMillis();

		//XXX we need to implement the reconnection of an user, it should be done with a check in the database,
		//but before we need to have a working account system.

		//if a name has not be defined the client listener don't send any message to the server
		//		while (!this.stopThread && !this.activeUser) {
		//			String line = null;
		//
		//			try {
		//				line = this.readFromSocket();
		//
		//				if (line != null && line.split(":")[0].equalsIgnoreCase("name")) {
		//					System.out.println("[MHServer] Received a name <<< " + line);
		//					//check if the name is available
		//					if(this.father.checkName(line.split(":")[1])) {
		//						//the name is available
		//						this.playerName = line.split(":")[1];
		//						this.father.addUser(this, new Player(this.playerName));
		//						this.activeUser = true;
		//					} else {
		//						//the name is already in use
		//						this.sendMsg("server:name already in use"); //XXX an error system should be better (e.g.: an int for every kind of error)
		//					}
		//					//XXX we should add a counter here, after few time of wrong answer the thread must die
		//				} /*else {
		//					//the first message isn't name, something has gone wrong, let him try again
		//					this.sendMsg("server:specify a name"); //like before, it is only temporary
		//				}*/
		//			} catch (IOException e) {
		//				if (this.clientSocket.isClosed()) {
		//					this.stopThread = true;
		//					System.out.println("[MHServer] Client socket has been closed");
		//				} else {
		//					e.printStackTrace();
		//					this.stopThread = true;
		//				}
		//			}
		//		}

		while (!this.stopThread) {

			String line = null;
			try {
				line = this.readFromSocket();

				if (line != null) {

					System.out.println("[MHServer] Receiving <<< " + line);
					//					this.sendMsg("Message: " + line + " RECEIVED");
					this.father.enqueueMessage(line, this);
				}
			} catch (IOException e) {
				if (this.clientSocket.isClosed()) {
					this.stopThread = true;
					System.out.println("[MHServer] Client socket has been closed");
				} else {
					e.printStackTrace();
					this.stopThread = true;
				}
			}
		}

		if (this.activeUser) {
			this.father.removeUser(this, this.playerName);
		}

	}

	/**
	 * This method is used to read a new line from the socket.
	 * 
	 * @return str The line received
	 * @throws IOException
	 */
	private String readFromSocket() throws IOException {

		char car = 0;
		String str = new String();
		int count = 0;

		count = this.reader.read();
		car = (char) count;
		str = str.concat(new Character(car).toString());

		while (count != -1) {

			try {
				count = this.reader.read();
			} catch (SocketException e) {
				System.out.println("[MHServer] Error reading from socket " + this.toString());
				return null;
			}
			car = (char) count;

			// "\n" marks end of message
			if (car == '\n') {
				if (str.endsWith("\r")) {
					return str.substring(0, str.length() - 1);
				} else {
					return str;
				}
			} else {
				str = str.concat(new Character(car).toString());
			}

		}

		/*
		 * if str is longer than one character return str trimmed else return an empty string (no message read!)
		 */
		if (str.length() > 1) {
			if (str.startsWith(" ")) {
				return str.substring(1);
			} else {
				return str;
			}
		} else {
			return null;
		}

	}

	public void sendMsg(String str) {

		try {
			this.writeOnSocket(str + "\r\n");
		} catch (SocketException e2) {
			System.out.println("Connection to client @" + this.clientSocket.getInetAddress().getHostAddress() + " is broken...");
		}

		System.out.println("[MHServer] >>>" + str);
	}

	/**
	 * 
	 * @param message
	 *            This method writes a message on socket
	 * @throws SocketException
	 */
	private void writeOnSocket(String message) throws SocketException {

		try {
			this.writer.write(message, 0, message.length());

			this.writer.flush();
		} /*
		 * catch (SocketException e2) { throw e2;
		 * 
		 * //myfather.getMessageListener().quitManage("Connection lost...serie 4th",this.getNick());
		 * //System.out.println("ERRORE IN SCRITTURA AL CLIENT: GESTIRE ECCEZIONE E CHIUDERE CLIENT!!!");
		 * //e2.printStackTrace(); }
		 */
		catch (IOException e) {

			e.printStackTrace();
		}
	}

}
